package sub2;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;

/**
 * Handles connections with clients
 * @see Server
 * @author Magali Moulin, Pierre Labiausse
 * @version 2
 */
public class ServerHandler extends Thread{
	private Socket clientSocket;
	private Server server;
	private Directory directory;
	ObjectOutputStream outObject = null;
	ObjectInputStream inObject=null;
	
	/**
	 * Constructor of this class 
	 * @param clientSocket : socket used for the connection
	 * @param directory: database in the server
	 * @param server: server which listen for the connection
	 */
	public ServerHandler(Socket clientSocket, Directory directory, Server server){
		this.clientSocket = clientSocket;
		this.directory = directory;
		this.server=server;
	}
	
	/**
	 * Action of the thread: read the data sent by the client,recognize the type of message received and answer to the client
	 */
	public void run(){
		System.out.println("Connection opened by the client");

		try {
			inObject = new ObjectInputStream(clientSocket.getInputStream());
			System.out.println("Server : inObject open");
			outObject=new ObjectOutputStream(clientSocket.getOutputStream());
		} catch (IOException e) {
			System.out.println(e.toString());
			return;
		}
		
		// The protocol is built around the sending of a single line by the client per step
			try {
				String received = null;
				try {
					received = (String)inObject.readObject();
					System.out.println(received);
				} catch (ClassNotFoundException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				if(received != null){
					// Client wants to register to the server
					if(received.equals("register")) {
						System.out.println("register");
						register(inObject);
					}
					if(received.equals("unregister")) {
						System.out.println("unregister");
						// Fetch the sharing port associated with the request
						int sharingPort = inObject.readInt();
						server.removeClient(new ClientTcpTuple(clientSocket.getInetAddress(),sharingPort));
						send(new String("OK"));	
					}
					
					if(received.equals("request")){
						ArrayList<String> request = (ArrayList<String>)inObject.readObject();
						System.out.println("request received : ");
						for(String _request : request){
							System.out.println("*"+_request+"*");
						}
						send(server.searchDirectory(request));
					}
					
					if(received.equals("list files")) {
						System.out.println("client asks the list of files on the server");
						ArrayList<String> result = new ArrayList<String>();
						HashMap<String, ArrayList<ClientTcpTuple>> _tmp =  directory.listFichier(); 
						for(String filename : _tmp.keySet()){
							result.add(filename+" ("+_tmp.get(filename).size()+")");
						}
						send(result);
						
					}
					if(received.equals("list registered clients")) {
						System.out.println("client asks the list of register clients");
						ArrayList<String> result = new ArrayList<String>();
						for (ClientTcpTuple tuple : directory.listPeer()){
							result.add(tuple.getInetAddress().getHostAddress()+":"+tuple.getPort());
						}
						
						send(result);
					}
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	
		
		// Closing streams and client socket, when the client decides to close the connection
		try {
			inObject.close();
			outObject.close();
			clientSocket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	private void register(ObjectInputStream inObject) {
		// Fetch the client's sharing Port
		int sharingPort = 0;
		try {
			sharingPort = inObject.readInt();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		// If the client is already registered, unregister him first
		server.removeClient(new ClientTcpTuple(clientSocket.getInetAddress(),sharingPort));				
		
		// Fetch the client's shared files
		LinkedList<String> sharedFiles = new LinkedList<String>();
		try {

			sharedFiles=(LinkedList<String>) inObject.readObject();
			for(String file : sharedFiles){
				System.out.println(file);
			}
			server.addnewClient(new ClientTcpTuple(clientSocket.getInetAddress(),sharingPort), sharedFiles);
			send(new String("OK"));
			System.out.println("Ok");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	private void send(Object objectSend) {
		try {	
			System.out.println("send");
			outObject.writeObject(objectSend);
			outObject.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * Getter to have access at the inetAddress of the client
	 * @return inetAddress
	 */
	public InetAddress getIpAddressClient(){
		return clientSocket.getInetAddress();
	}
}
